﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Xml;
using System.Windows.Media;
using System.Collections;
using System.Windows.Media.Imaging;

namespace DiagramDesigner
{
    public partial class DesignerCanvas : Canvas
    {

        public static bool GetIsSelected(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsSelectedProperty);
        }

        public static void SetIsSelected(DependencyObject obj, bool value)
        {
            obj.SetValue(IsSelectedProperty, value);
        }

        // Using a DependencyProperty as the backing store for IsSelected.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsSelectedProperty =
            DependencyProperty.RegisterAttached("IsSelected", typeof(bool), typeof(DesignerCanvas),
            new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsArrange));



        public CanvasTool Tool
        {
            get { return (CanvasTool)GetValue(ToolProperty); }
            set { SetValue(ToolProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Tool.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ToolProperty =
            DependencyProperty.Register("Tool", typeof(CanvasTool), typeof(DesignerCanvas), new UIPropertyMetadata(null));        
        

        public BitmapSource BgImage
        {
            get { return (BitmapSource)GetValue(BgImageProperty); }
            set { SetValue(BgImageProperty, value); }
        }

        // Using a DependencyProperty as the backing store for BgImage.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty BgImageProperty =
            DependencyProperty.Register("BgImage", typeof(BitmapSource), typeof(DesignerCanvas), new UIPropertyMetadata(null, BgImageChangedCallback));

        private static void BgImageChangedCallback(DependencyObject obj,
          DependencyPropertyChangedEventArgs e)
        {
            DesignerCanvas control = ((DesignerCanvas)obj);
            if (control.BgImage != null)
            {                                
                int width = control.BgImage.PixelWidth;
                int height = control.BgImage.PixelHeight;

                int stride = width * 4; // 4 bytes per pixel
                byte[] pixelData = new byte[stride * height];
                control.BgImage.CopyPixels(pixelData, stride, 0);

                //Matrix matrix = PresentationSource.FromVisual(control).CompositionTarget.TransformToDevice;
                //double dpiX = matrix.M11*96;
                //double dpiY = matrix.M22*96;

                BitmapSource bmpSource = BitmapSource.Create(width, height, 96, 96,
                    control.BgImage.Format, control.BgImage.Palette, pixelData, stride);

                control.Width = width;
                control.Height = height;

                ImageBrush ib = new ImageBrush(bmpSource);                
                ib.Stretch = Stretch.None;
                control.Background = ib;                  
            }
            else
                control.Background = null;
        }

        //tricky disabling of returning background as hit test result
        protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
        {
            //HitTestResult htr = base.HitTestCore(hitTestParameters);
            //if (htr.VisualHit == this)
            if (Tool != null)
                return base.HitTestCore(hitTestParameters);
            else
                return null;
            //else
            //    return htr;
        }

        //protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
        //{
        //    base.OnVisualChildrenChanged(visualAdded, visualRemoved);
                        
        //    //if (visualAdded is FrameworkElement)
        //    //{
        //    //    ((FrameworkElement)visualAdded).Parent = this;
        //    //}
        //}        
        //private Point? rubberbandSelectionStartPoint = null;

        private SelectionService selectionService;
        internal SelectionService SelectionService
        {
            get
            {
                if (selectionService == null)
                    selectionService = new SelectionService(this);

                return selectionService;
            }
        }

        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            base.OnMouseDown(e);

            Mouse.Capture(this);

            if (Tool != null)
            {
                Tool.DragStarted(e.GetPosition(this));                
            }

            e.Handled = true;
           
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            
            if (Tool != null)
            {
                Tool.MouseMove(e.GetPosition(this));                
                this.InvalidateArrange();
            }

            e.Handled = true;
        }
        protected override void OnMouseUp(MouseButtonEventArgs e)
        {
            base.OnMouseUp(e);

            Mouse.Capture(null);

            if (Tool != null)
            {
                Tool.DragCompleted(e.GetPosition(this));
            }

            e.Handled = true;
        }

        protected override void OnDrop(DragEventArgs e)
        {
            base.OnDrop(e);
            DragObject dragObject = e.Data.GetData(typeof(DragObject)) as DragObject;
            if (dragObject != null && !String.IsNullOrEmpty(dragObject.Xaml))
            {
                DesignerItem newItem = null;
                Object content = XamlReader.Load(XmlReader.Create(new StringReader(dragObject.Xaml)));

                if (content != null)
                {
                    newItem = new DesignerItem();
                    newItem.Content = content;

                    Point position = e.GetPosition(this);

                    if (dragObject.DesiredSize.HasValue)
                    {
                        Size desiredSize = dragObject.DesiredSize.Value;
                        newItem.Width = desiredSize.Width;
                        newItem.Height = desiredSize.Height;

                        DesignerCanvas.SetLeft(newItem, Math.Max(0, position.X - newItem.Width / 2));
                        DesignerCanvas.SetTop(newItem, Math.Max(0, position.Y - newItem.Height / 2));
                    }
                    else
                    {
                        DesignerCanvas.SetLeft(newItem, Math.Max(0, position.X));
                        DesignerCanvas.SetTop(newItem, Math.Max(0, position.Y));
                    }

                    Canvas.SetZIndex(newItem, this.Children.Count);
                    this.Children.Add(newItem);
                    //SetConnectorDecoratorTemplate(newItem);

                    //update selection
                    this.SelectionService.SelectItem(newItem);
                    newItem.Focus();
                }

                e.Handled = true;
            }
        }

        protected override Size MeasureOverride(Size constraint)
        {
            Size size = new Size();

            foreach (UIElement element in this.InternalChildren)
            {
                double left = Canvas.GetLeft(element);
                double top = Canvas.GetTop(element);
                left = double.IsNaN(left) ? 0 : left;
                top = double.IsNaN(top) ? 0 : top;

                //measure desired size for each child
                element.Measure(constraint);

                Size desiredSize = element.DesiredSize;
                if (!double.IsNaN(desiredSize.Width) && !double.IsNaN(desiredSize.Height))
                {
                    size.Width = Math.Max(size.Width, left + desiredSize.Width);
                    size.Height = Math.Max(size.Height, top + desiredSize.Height);
                }
            }
            // add margin 
            size.Width += this.Margin.Right;
            size.Height += this.Margin.Bottom;
            return size;
        }

        protected override void OnRender(DrawingContext dc)
        {
            base.OnRender(dc);

            //if (Tool != null)
            //    Tool.RenderGraphic(dc);
        }

    }


}
